﻿using System;
using System.Collections.Generic;
using System.Text;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Excel;
using SdmxMl.Manager;
namespace SasPlugIn.DB
{
    public enum DsdExportMode
    {
        Linear,
        ObservationAssigned,
        SubConceptFlag,
        
    }
    public class DataStructureDefTable : DbTable
    {
        #region Internal Members

        /// <summary> DSD to operate with </summary>
        private KeyFamilyType dsd;

        /// <summary>Helper to obtains refered element of each dimension </summary>
        private RefVarHelper refVar;

        /// <summary>List of dsd elements with or without Flag Attributes </summary>
        private List<BaseDimensionType> listVar;

        /// <summary> Mode of operation during columns generation </summary>
        private DsdExportMode exportMode;

        /// <summary> In memory artefacts + loader </summary>
        private SmManager sm;

        private Dictionary<string, DbColumnAttachedCodelist> dicoAttachedCodelists;

        #endregion

        public Dictionary<string, DbColumnAttachedCodelist> BindedCodelists { get; private set; }


        #region Constructor 

        /// <summary>
        /// C'tor for generation of an empty SAS table according to SDMX DSD
        /// </summary>
        /// <param name="ArtefactDsd">a SDMX DSD</param>
        /// <param name="smMgr">artefact container to use</param>
        /// <param name="dsdMode">mode of interpretation for dsd attributes</param>
        /// <param name="anInv">annotation inventory (according to user selection)</param>
        /// <param name="tableName">Name of DB table</param>
        /// <param name="prefix">Prefix for DB table</param>
        public DataStructureDefTable(KeyFamilyType ArtefactDsd, SmManager smMgr, DsdExportMode dsdMode, AnnotationInventory anInv, string tableName, string prefix)
            : base(ArtefactDsd, anInv, tableName, prefix)
        {
            sm = smMgr == null ? ReferenceManager.GetSmContainerImage() : smMgr;

            exportMode = dsdMode;
        
            Create();
        }

       #endregion

        /// <summary>
        /// Fill Header columns according to artefact attributes
        /// </summary>
        protected override void CreateColumns()
        {
            dsd = artefact as KeyFamilyType;

            dicoAttachedCodelists = new Dictionary<string, DbColumnAttachedCodelist>();

            // In ObservationAssigned mode, store columns holding assignation and the associated codelist
            Dictionary<AttributeType, CodeListType> obsAssignedAttList = new Dictionary<AttributeType, CodeListType>();

            // Create helper
            refVar = new RefVarHelper(sm);
            // Prepare list, ignore flag speciality
            listVar = refVar.PrepareVarList(dsd, false);

            // For all items of DSD
            foreach (BaseDimensionType bdt in listVar)
            {
                // Associated codelist (direct or via concept)
                CodeListType clt = refVar.GetCodelistOfBaseDim(bdt);

                AttributeType att = bdt as AttributeType;
                // skip non observational attributes and flag attributes in certain modes
                if (att != null && exportMode == DsdExportMode.ObservationAssigned)
                {
                    if (att.AttachmentLevel != AttributeType.AttachmentLevelType.Observation &&
                        att.AttachmentLevel != AttributeType.AttachmentLevelType.Series)
                        continue;

                    if (exportMode == DsdExportMode.ObservationAssigned)
                    {
                        if (att.AttachmentMeasureList.Count > 0)
                        {
                            obsAssignedAttList.Add(att, clt);
                            continue;
                        }
                    }
                }

                // Get associated var type (direct in DSD or via concept if missing in DSD)
                TextFormatType tft = refVar.GetTextFormatTypeOfBaseDim(bdt);
                if (tft.TextType == TextTypeType.Undefined)
                {
                    if (clt != null)
                        tft = clt.GetDefaultTextFormat();
                }

                // Quality list via flag ?
                CodeListType cltQuality = null;
                if (exportMode == DsdExportMode.SubConceptFlag)
                {
                    // locate referred concept check if has child with same id ended with "_F"
                    ConceptSchemeType cst = refVar.GetConceptScheme(bdt.ConceptSchemeInfo);
                    ConceptType cFlag = cst.ConceptList.LocateItem(bdt.ConceptInfo.Id + "_F") as ConceptType;
                    if (cFlag != null && cFlag.Parent == bdt.ConceptInfo.Id && cFlag.CoreRepresentation.IsFilled)
                    {
                        cltQuality = refVar.GetCodelist(cFlag.CoreRepresentation);
                    }

                }
                // Create DSD column
                columns.Add(new DbColumn(bdt.Id, tft));
                if (clt != null)
                    dicoAttachedCodelists.Add(bdt.Id, new DbColumnAttachedCodelist(bdt.Id, clt, cltQuality, tft));
            }

            // second path add secondary codelists
            if (exportMode == DsdExportMode.ObservationAssigned)
            {
                foreach (KeyValuePair<AttributeType, CodeListType> kvp in obsAssignedAttList)
                {
                    // add secondaty codelist to all column refered
                    foreach (string s in kvp.Key.AttachmentMeasureList)
                    {
                        // No main codelist or add as secondary
                        if (dicoAttachedCodelists.ContainsKey(s) == false)
                            dicoAttachedCodelists.Add(s, new DbColumnAttachedCodelist(s, kvp.Value, null, null));
                        else
                        {
                            dicoAttachedCodelists[s] = new DbColumnAttachedCodelist(dicoAttachedCodelists[s].ColName, dicoAttachedCodelists[s].CltMain, kvp.Value, dicoAttachedCodelists[s].TFormat);
                        }
                    }
                    listVar.Remove(kvp.Key);
                }
            }

            // Now create dico with main codelist fullId key and check duplicates
            BindedCodelists = new Dictionary<string, DbColumnAttachedCodelist>();
            foreach (KeyValuePair<string, DbColumnAttachedCodelist> kvp in dicoAttachedCodelists)
            {
                if (BindedCodelists.ContainsKey(kvp.Value.CltMain.FullIdent))
                {
                    // check same secondary the two null or the two same fullId
                    if (GetFullIdent(BindedCodelists[kvp.Value.CltMain.FullIdent].CltSecondary) !=
                        GetFullIdent(kvp.Value.CltSecondary))
                    {
                        throw new ApplicationException(string.Format("Invalid, Codelist {0} aggregated with different codelists [{1}] and [{2}]", kvp.Value.CltMain.FullIdent,
                            GetFullIdent(BindedCodelists[kvp.Value.CltMain.FullIdent].CltSecondary), GetFullIdent(kvp.Value.CltSecondary)));
                    }
                }
                else
                {
                    BindedCodelists.Add(kvp.Value.CltMain.FullIdent, kvp.Value);
                }
            }

        }

        private string GetFullIdent(CodeListType clt)
        {
            string fid = string.Empty;
            if (clt != null)
                fid = clt.FullIdent;
            return fid;
        }
 

        protected override void FillData()
        {
            // NO data
        }


    }
}
